﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using H3Sdk.Attachment;
using H3Sdk.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace H3Sdk.Http
{
    public class ApiClient
    {
        private readonly string _engineCode;
        private readonly string _engineSecret;
        private readonly string _baseUrl = "https://www.h3yun.com";
        private readonly string _invokeUrl = "OpenApi/Invoke";
        private readonly string _downloadUrl = "Api/DownloadBizObjectFile";
        private readonly string _uploadUrl = "OpenApi/UploadAttachment";

        public ApiClient(string engineCode, string engineSecret)
        {
            _engineCode = engineCode;
            _engineSecret = engineSecret;
        }

        /// <summary>
        /// post基础请求
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="appCode"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        private async Task<HttpResponseMessage> PostBaseAsync(string controller, string actionName, string appCode,
            Dictionary<string, object> body)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("EngineCode", _engineCode);
                client.DefaultRequestHeaders.Add("EngineSecret", _engineSecret);
                client.BaseAddress = new Uri(_baseUrl);
                body.Add("Controller", controller);
                body.Add("ActionName", actionName);
                body.Add("AppCode", appCode);
                var data = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
                var result = await client.PostAsync(_invokeUrl, data);
                return result;
            }
        }

        /// <summary>
        /// post基础请求
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="actionName"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        private async Task<HttpResponseMessage> PostBaseAsync(string schemaCode, string actionName,
            Dictionary<string, object> body)
        {
            using( var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Add("EngineCode", _engineCode);
                client.DefaultRequestHeaders.Add("EngineSecret", _engineSecret);
                client.BaseAddress = new Uri(_baseUrl);
                body.Add("SchemaCode", schemaCode);
                body.Add("ActionName", actionName);
                var data = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json");
                return await client.PostAsync(_invokeUrl, data);
            }
        }

        /// <summary>
        /// post基础请求，针对download url
        /// </summary>
        /// <param name="body"></param>
        /// <returns></returns>
        private async Task<HttpResponseMessage> PostBaseAsync(Dictionary<string, string> body)
        {
            using( var client = new HttpClient())
            {
                body.Add("EngineCode", _engineCode);
                FormUrlEncodedContent content = new FormUrlEncodedContent(body);
                content.Headers.Add("EngineCode", _engineCode);
                content.Headers.Add("EngineSecret", _engineSecret);
                content.Headers.Add("ContentType", "application/json; charset=utf-8");
                client.BaseAddress = new Uri(_baseUrl);
                return await client.PostAsync(_downloadUrl, content);
            }
        }

        /// <summary>
        /// post请求，返回response
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="actionName"></param>
        /// <param name="body"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <exception cref="H3Exception"></exception>
        public async Task<Response<T>> PostResponseAsync<T>(string schemaCode, string actionName,
            Dictionary<string, object> body)
        {
            var response = await PostBaseAsync(schemaCode, actionName, body);
            var result = JsonConvert.DeserializeObject<Response<T>>(await response.Content.ReadAsStringAsync());
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new H3Exception(result.ErrorMessage);
            }

            return result;
        }

        /// <summary>
        /// post请求，返回Response
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="actionName"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        /// <exception cref="H3Exception"></exception>
        public async Task<Response> PostResponseAsync(string schemaCode, string actionName,
            Dictionary<string, object> body)
        {
            var response = await PostBaseAsync(schemaCode, actionName, body);
            var result = JsonConvert.DeserializeObject<Response>(await response.Content.ReadAsStringAsync());
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new H3Exception(result.ErrorMessage);
            }

            return result;
        }
        
        private async Task<Response> PostResponseForUploadAsync(Stream fileStream,string fileName,Dictionary<string,string> query)
        {
            using (var client=new HttpClient())
            {
                client.DefaultRequestHeaders.Add("EngineCode", _engineCode);
                client.DefaultRequestHeaders.Add("EngineSecret", _engineSecret);
                client.BaseAddress = new Uri(_baseUrl);
                var formData = new MultipartFormDataContent();
                formData.Add(new StreamContent(fileStream),fileName.Split('.')[0],fileName);
                foreach (var item in query)
                {
                    formData.Add(new StringContent(item.Value), item.Key);
                }
                var response=await client.PostAsync(_uploadUrl,formData);
                var result = JsonConvert.DeserializeObject<Response>(await response.Content.ReadAsStringAsync());
                if (!string.IsNullOrEmpty(result.ErrorMessage))
                {
                    throw new H3Exception(result.ErrorMessage);
                }

                return result;
            }
        }

        /// <summary>
        /// post请求，返回ReturnData
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="actionName"></param>
        /// <param name="body"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<ReturnData<T>> PostAsync<T>(string schemaCode, string actionName,
            Dictionary<string, object> body)
        {
            var response = await PostBaseAsync(schemaCode, actionName, body);
            var result = JsonConvert.DeserializeObject<Response<T>>(await response.Content.ReadAsStringAsync());
            return result.ReturnData;
        }

        public async Task<ReturnData<object>> PostAsync(string schemaCode, string actionName,
            Dictionary<string, object> body)
        {
            return await PostAsync<object>(schemaCode, actionName, body);
        }


        /// <summary>
        /// post请求，返回Response
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="appCode"></param>
        /// <param name="body"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <exception cref="H3Exception"></exception>
        public async Task<Response<T>> PostResponseAsync<T>(string controller, string actionName, string appCode,
            Dictionary<string, object> body)
        {
            var response = await PostBaseAsync(controller, actionName, appCode, body);
            var result = JsonConvert.DeserializeObject<Response<T>>(await response.Content.ReadAsStringAsync());
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new H3Exception(result.ErrorMessage);
            }

            return result;
        }

        /// <summary>
        /// Post请求，返回Response
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="appCode"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        /// <exception cref="H3Exception"></exception>
        public async Task<Response> PostResponseAsync(string controller, string actionName, string appCode,
            Dictionary<string, object> body)
        {
            var response = await PostBaseAsync(controller, actionName, appCode, body);
            var result = JsonConvert.DeserializeObject<Response>(await response.Content.ReadAsStringAsync());
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new H3Exception(result.ErrorMessage);
            }

            return result;
        }

        /// <summary>
        /// Post请求数据，返回ReturnData
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="appCode"></param>
        /// <param name="body"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public async Task<ReturnData<T>> PostAsync<T>(string controller, string actionName, string appCode,
            Dictionary<string, object> body)
        {
            var result = await PostResponseAsync<T>(controller, actionName, appCode, body);
            return result.ReturnData;
        }

        /// <summary>
        /// post请求数据，返回JToken
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="appCode"></param>
        /// <param name="body"></param>
        /// <returns></returns>
        public async Task<JToken> PostAsync(string controller, string actionName, string appCode,
            Dictionary<string, object> body)
        {
            var result = await PostResponseAsync(controller, actionName, appCode, body);
            return result.ReturnData;
        }

        /// <summary>
        /// 查询业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="objectId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<JToken> LoadBizObjectAsync(string schemaCode, string objectId)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("BizObjectId", objectId);
            var result = await PostResponseAsync(schemaCode, "LoadBizObject", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }

            if (result.ReturnData != null)
            {
                return result.ReturnData["BizObject"];
            }

            return null;
        }

        /// <summary>
        /// 批量查询业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<JToken> LoadBizObjectsAsync(string schemaCode, Filter.Filter filter)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("Filter", JsonConvert.SerializeObject(filter));
            var result = await PostResponseAsync(schemaCode, "LoadBizObjects", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }

            if (result.ReturnData != null)
            {
                return result.ReturnData["BizObjectArray"];
            }

            return null;
        }

        /// <summary>
        /// 创建业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="isSubmit"></param>
        /// <param name="bizObject"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<string> CreateBizObjectAsync(string schemaCode, bool isSubmit,
            Dictionary<string, object> bizObject)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("IsSubmit", isSubmit);
            body.Add("BizObject", JsonConvert.SerializeObject(bizObject));
            var result = await PostResponseAsync(schemaCode, "CreateBizObject", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }

            if (result.ReturnData != null)
            {
                return result.ReturnData["BizObjectId"].Value<string>();
            }

            return null;
        }

        /// <summary>
        /// 批量创建业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="isSubmit"></param>
        /// <param name="bizObjects"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<List<string>> CreateBizObjectsAsync(string schemaCode, bool isSubmit,
            List<Dictionary<string, object>> bizObjects)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("IsSubmit", isSubmit);
            body.Add("BizObjectArray", BizObjectListToStrList(bizObjects));
            var result = await PostResponseAsync(schemaCode, "CreateBizObjects", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }

            if (result.ReturnData != null)
            {
                return result.ReturnData["BizObjectIdArray"].Values<string>().ToList();
            }

            return null;
        }

        /// <summary>
        /// 更新业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="objectId"></param>
        /// <param name="bizObject"></param>
        /// <exception cref="Exception"></exception>
        public async Task UpdateBizObjectAsync(string schemaCode, string objectId, Dictionary<string, object> bizObject)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("BizObjectId", objectId);
            body.Add("BizObject", JsonConvert.SerializeObject(bizObject));
            var result = await PostResponseAsync(schemaCode, "UpdateBizObject", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }
        }

        /// <summary>
        /// 删除业务对象
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="objectId"></param>
        /// <exception cref="Exception"></exception>
        public async Task RemoveBizObjectAsync(string schemaCode, string objectId)
        {
            Dictionary<string, object> body = new Dictionary<string, object>();
            body.Add("BizObjectId", objectId);
            var result = await PostResponseAsync(schemaCode, "RemoveBizObject", body);
            if (!string.IsNullOrEmpty(result.ErrorMessage))
            {
                throw new Exception(result.ErrorMessage);
            }
        }

        /// <summary>
        /// 下载附件
        /// </summary>
        /// <param name="schemaCode"></param>
        /// <param name="objectId"></param>
        /// <param name="bizProperties"></param>
        /// <param name="fieldName"></param>
        /// <param name="attachmentId"></param>
        /// <returns></returns>
        public async Task<FileObject> DownloadBizObjectFileAsync(string schemaCode, string objectId,
            Dictionary<string, object> bizProperties, string fieldName, string attachmentId)
        {
            Dictionary<string, string> body = new Dictionary<string, string>();
            body.Add("attachmentId", attachmentId);
            var result = await PostBaseAsync(body);
            Stream responseStream = await result.Content.ReadAsStreamAsync();
            var contents = result.Content.Headers.GetValues("Content-Disposition").ToArray();
            if (contents.Length > 0)
            {
                string contentName = contents[0];
                string fileName = System.Web.HttpUtility.UrlDecode(contentName.Split(';')[1].Split('=')[1],
                    System.Text.Encoding.UTF8);
                string contentType = result.Content.Headers.GetValues("Content-Type").ToArray()[0];
                byte[] fileBytes;
                using (BinaryReader binaryReader = new BinaryReader(responseStream))
                {
                    fileBytes = binaryReader.ReadBytes((int)responseStream.Length);
                }

                FileObject file = new FileObject();
                file.FileId = attachmentId;
                file.FileName = fileName;
                file.Content = fileBytes;
                file.ContentType = contentType;
                file.SchemaCode = schemaCode;
                file.BizObjectId = objectId;
                file.BizProperties = bizProperties;

                return file;
            }

            return null;
        }

        public async Task<string> UploadBizObjectFileAsync(string schemaCode, string objectId, string fieldName,
            string fileName, FileStream fileStream)
        {
            Dictionary<string, string> query = new Dictionary<string, string>();
            query.Add("SchemaCode",schemaCode);
            query.Add("FilePropertyName",fieldName);
            query.Add("BizObjectId",objectId);
            var result=await PostResponseForUploadAsync(fileStream, fileName, query);
            return result.ReturnData["AttachmentId"].Value<string>();
        }

        /// <summary>
        /// 序列化bizObject list
        /// </summary>
        /// <param name="bizObjects"></param>
        /// <returns></returns>
        private object BizObjectListToStrList(List<Dictionary<string, object>> bizObjects)
        {
            List<string> result = new List<string>();
            foreach (var obj in bizObjects)
            {
                result.Add(JsonConvert.SerializeObject(obj));
            }

            return result;
        }
    }
}